package org.darcy.config.shiro;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.darcy.config.shiro.realm.ShiroRealm;
import org.darcy.entity.SysUser;
import org.darcy.framework.blog.exception.BlogException;
import org.darcy.framework.blog.holder.SpringContextHolder;
import org.darcy.model.SysResourcesModel;
import org.darcy.service.SysResourcesService;
import org.darcy.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * Shiro-权限相关
 */
@Slf4j
@Service
public class ShiroService {

	@Autowired
	private SysResourcesService resourcesService;
	
	@Autowired
	private SysUserService userService;

	/**
	 * 初始化权限
	 */
	public Map<String, String> loadFilterChainDefinitions() {
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
		filterChainDefinitionMap.put("/admin/security/logout", "logout");
		filterChainDefinitionMap.put("/admin/security/**", "anon");
		// 加载数据库中配置的资源权限列表
		List<SysResourcesModel> resourcesList = resourcesService.listUrlAndPermission();
		if (CollectionUtils.isEmpty(resourcesList)) {
			throw new BlogException("未加载到resources内容，请确认是否执行了init_data.sql");
		}
		for (SysResourcesModel resources : resourcesList) {
			if (!StringUtils.isEmpty(resources.getUrl()) && !StringUtils.isEmpty(resources.getPermission())) {
				String permission = "perms[" + resources.getPermission() + "]";
				filterChainDefinitionMap.put(resources.getUrl(), permission);
			}
		}
		filterChainDefinitionMap.put("/admin/**", "user");
		return filterChainDefinitionMap;
	}

	/**
	 * 重新加载权限
	 */
	public void updatePermission() {
		ShiroFilterFactoryBean shirFilter = SpringContextHolder.getBean(ShiroFilterFactoryBean.class);
		synchronized (shirFilter) {
			AbstractShiroFilter shiroFilter = null;
			try {
				shiroFilter = (AbstractShiroFilter) shirFilter.getObject();
			} catch (Exception e) {
				throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
			}

			PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
					.getFilterChainResolver();
			DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

			// 清空老的权限控制
			manager.getFilterChains().clear();

			shirFilter.getFilterChainDefinitionMap().clear();
			shirFilter.setFilterChainDefinitionMap(loadFilterChainDefinitions());
			// 重新构建生成
			Map<String, String> chains = shirFilter.getFilterChainDefinitionMap();
			for (Map.Entry<String, String> entry : chains.entrySet()) {
				String url = entry.getKey();
				String chainDefinition = entry.getValue().trim().replace(" ", "");
				manager.createChain(url, chainDefinition);
			}
		}
	}

	/**
	 * 重新加载用户权限
	 *
	 * @param user
	 */
	private void reloadAuthorizingByUserId(SysUser user) {
		RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
		ShiroRealm shiroRealm = (ShiroRealm) rsm.getRealms().iterator().next();
		Subject subject = SecurityUtils.getSubject();
		String realmName = subject.getPrincipals().getRealmNames().iterator().next();
		SimplePrincipalCollection principals = new SimplePrincipalCollection(user, realmName);
		subject.runAs(principals);
		shiroRealm.getAuthorizationCache().remove(subject.getPrincipals());
		subject.releaseRunAs();
		log.info("用户[{}]的权限更新成功！！", user.getUsername());
	}

	/**
	 * 重新加载所有拥有roleId角色的用户的权限
	 *
	 * @param roleId
	 */
	public void reloadAuthorizingByRoleId(Long roleId) {
		List<SysUser> userList = userService.listByRoleId(roleId);
		if (CollectionUtils.isEmpty(userList)) {
			return;
		}
		for (SysUser user : userList) {
			reloadAuthorizingByUserId(user);
		}
	}

}
